package com.linjiang;

import javax.servlet.Servlet;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Tomcat {

    /**
     * 应用容器map
     */
    private Map<String, Context> contextMap = new HashMap<>();

    public Map<String, Context> getContextMap() {
        return contextMap;
    }

    public void start(){
        // Socket连接 TCP
        try {
            ExecutorService threadPool = Executors.newFixedThreadPool(20);
            ServerSocket serverSocket = new ServerSocket(8081);
            while (true){
                //不断的处理socket连接
                Socket socket = serverSocket.accept();
                //使用线程池处理socket连接
                threadPool.execute(new SocketProcessor(socket, this));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        Tomcat tomcat = new Tomcat();
        //部署应用
        tomcat.deployApps();
        //启动tomcat
        tomcat.start();
    }

    /**
     * 部署应用，解析获取webapps目录下各应用的所有Servlet类
     */
    private void  deployApps(){
        File webapps = new File(System.getProperty("user.dir"), "tomcat-learn/webapps");
        for (String appName : webapps.list()) {
            //加载当前文件夹下的Servlet类
            deployApp(webapps, appName);
        }
    }

    private void deployApp(File webapps, String appName){
        // 先拿到classes⽬录
        File appDirectory = new File(webapps, appName);
        File classesDirectory = new File(appDirectory, "classes");
        // 应用的servlet容器
        Context context = new Context(appName);
        // 拿到该⽬录下的所有class⽂件
        List<File> classes = getAllFilePath(classesDirectory);
        WebappClassLoader classLoader = null;
        try {
            classLoader = new WebappClassLoader(new URL[]{classesDirectory.toURL()});
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
        for (File clazz : classes) {
            //判断后缀，是否是class文件
            System.out.println(clazz.getPath());
            if (clazz.getName().contains(".class")){
                String name = clazz.getPath();
                name = name.replace(classesDirectory.getPath() + "\\", "");
                name = name.replace(".class", "");
                name = name.replace("\\", ".");
                //com.linjiang.LinJiangServlet
                try {
                    //使用自定义类加载器加载当前类
                    Class<?> aClass = classLoader.loadClass(name);
                    //判断当前类是不是继承HttpServlet
                    if (HttpServlet.class.isAssignableFrom(aClass)){
                        //判断当前类是否有@WebServlet注解
                        if (aClass.isAnnotationPresent(WebServlet.class)){
                            //获取注解属性
                            WebServlet annotation = aClass.getAnnotation(WebServlet.class);
                            for (String url : annotation.urlPatterns()) {
                                //往应用容器中添加映射关系
                                context.addUrlPatternMapping(url, (Servlet) aClass.newInstance());
                            }
                        }
                    }
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        //将该应用的servlet容器放入tomcat应用容器map中
        contextMap.put(appName, context);
    }

    /**
     * 递归获取该目录下的所有文件
     * @param srcFile
     * @return
     */
    public List<File> getAllFilePath(File srcFile) {
        List<File> result = new ArrayList<>();
        File[] files = srcFile.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    result.addAll(getAllFilePath(file));
                } else {
                    result.add(file);
                }
            }
        }
        return result;
    }
}